﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Collections;
using System.Collections.ObjectModel;
using System.ComponentModel;

namespace LiveLinq.Internal
{

	class ObservableSafeDownCast<T, TSource> : ObservableCast<T> where TSource : T
	{
		IEnumerable<TSource> _source;
		IList<TSource> _sourceAsList;

		public ObservableSafeDownCast(IEnumerable<TSource> source)
		{
			if (source == null)
			{
				throw new ArgumentNullException("source");
			}
			_source = source;
			_sourceAsList = source as IList<TSource>;
			_sourceNotifiableCollection = source as INotifyCollectionChanged;
			_sourceNotifiableObject = source as INotifyPropertyChanged;
			if (_sourceNotifiableCollection == null)
			{
				throw new ArgumentException("ObservableSafeDownCast: source is not notifiable");
			}
			CopyFromFource();
			_sourceNotifiableCollection.CollectionChanged += SourceCollectionChanged;
		}

		void SourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
		{
			switch (e.Action)
			{
				case NotifyCollectionChangedAction.Add:
					if (e.NewItems.Count == 1)
					{
						//Insert(e.NewStartingIndex, (T)e.NewItems[0]);
					}
					else
					{
						throw new NotImplementedException();
						// int i = e.NewStartingIndex;
						// foreach (T item in e.NewItems)
						// {
						//    Insert(i++, item);
						// }
					}
					break;
				case NotifyCollectionChangedAction.Remove:
					if (e.OldItems.Count == 1)
					{
						if (!Equals(this[e.OldStartingIndex], e.OldItems[0]))
						{
							throw SyncFailed;
						}
						RemoveAt(e.OldStartingIndex);
					}
					else
					{
						throw new NotImplementedException();
					}
					break;
				case NotifyCollectionChangedAction.Move:
				case NotifyCollectionChangedAction.Replace:
				case NotifyCollectionChangedAction.Reset:
				default:
					var cnt = Count;
					CheckReentrancy();
					Items.Clear();
					CopyFromFource();
					if (cnt != Count)
					{
						OnPropertyChanged(_pcCount);
					}
					OnPropertyChanged(_pcIndexer);
#warning it is not safe to resend event args with items from another collection!!
					OnCollectionChanged(e);
					break;
			}
			if (_sourceAsList != null)
			{
				if (_sourceAsList.Count != Count)
				{
					throw SyncFailed;
				}
				var index = e.NewStartingIndex == -1 ? e.OldStartingIndex : e.NewStartingIndex;
				if (index != -1)
				{
					for (int i = index - 2, m = index + 2; i < Count; i++)
					{
						if (i < 0) continue;
						if (!Equals(_sourceAsList[i], this[i]))
						{
							throw SyncFailed;
						}
					}
				}
			}
		}

		private static Exception SyncFailed
		{
			get
			{
				throw new Exception("List synchronization failed");
			}
		}

		void CopyFromFource()
		{
			foreach (var item in _source)
			{
				Items.Add(item);
			}
		}
	}
}
